/*
* License:  This  program  is  free  software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the  Free Software Foundation; either version 3 of the License, or (at your
* option)  any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*/

package pcgen.CharacterViewer.resources;

import java.lang.reflect.Constructor;
import java.util.ArrayList;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.json.JSONException;
import org.json.JSONObject;

import android.os.Parcel;
import android.os.Parcelable;

import pcgen.android.JSONUtils;
import pcgen.android.Logger;

import pcgen.CharacterViewer.AppActivity;
import pcgen.CharacterViewer.R;
import pcgen.CharacterViewer.resources.GameResource.ResourceTypes;
import pcgen.CharacterViewer.utility.IResource;

public class GameFolder extends GameResourceParcelable
{
	public GameFolder()
	{
	}
	
	public GameFolder(Parcel in)
	{
		super(in);
	}
	
	public GameFolder(String name)
	{
		_name = name;
		_resources = new GameResourceList();
	}
	
	@SuppressWarnings("unchecked")
	public static GameFolder newInstance(AppActivity activity, String type, String name) 
		throws Throwable
	{
		Validate.notNull(activity);
		Validate.notEmpty(type);
		Validate.notEmpty(name);
		
		String className = activity.getApplicationApp().getGameTypes().getClass(type);
		if (StringUtils.isEmpty(className))
			return null;
	
		Class classInterface = Class.forName(className);
		Constructor constructor = classInterface.getConstructor(new Class[] { String.class });
		GameFolder folder = (GameFolder)constructor.newInstance(new Object[] { name });
		folder.setName(name);
		folder.setType(type);
		return folder;
	}
	
	public String getFileName()
	{
		return String.format("%s.%s", getName(), getType());
	}
	
	public String getName()
	{
		return _name;
	}
	
	public GameResource addResource(String name, String location, ResourceTypes type)
		throws Throwable
	{
		GameResource resource = null;//new GameResource();
		if (type == ResourceTypes.Character)
			resource = new GameResourceCharacter();
		else
			resource = new GameResource();
		resource.setName(name);
		resource.setLocation(location);
		resource.setType(type);
		resource.init(this);
		resource.initFromLocation();
		
		if (type == ResourceTypes.Character)
		{
			if (!isType(((GameResourceCharacter)resource).getGameType()))
				return null;
		}
		
		addResource(resource);
		return resource;
	}
	
	public void addResource(GameResource resource)
	{
		_resources.add(resource);
	}
	
	public int checkConditionAttack(GameResourceAttributeAttack attribute, int value)
	{
		if (attribute.getCharacter().hasCondition(GameTypesCondition.Dazzeled))
			value -= 1;
			
		if (attribute.getCharacter().hasCondition(GameTypesCondition.Entangled) ||
			attribute.getCharacter().hasCondition(GameTypesCondition.Frightened)||
			attribute.getCharacter().hasCondition(GameTypesCondition.Shaken) ||
			attribute.getCharacter().hasCondition(GameTypesCondition.Sickened))
			value -= 2;
		
		if (attribute.getCharacter().hasCondition(GameTypesCondition.Prone))
			value -= 4;
		
		return value;
	}
	
	public int checkConditionDefense(GameResourceAttributeDefense attribute, int value, boolean hasDexterity)
	{
		GameResourceCharacter character = attribute.getCharacter();
		
		if (character.hasCondition(GameTypesCondition.Blinded) ||
			character.hasCondition(GameTypesCondition.Cowering) ||
			character.hasCondition(GameTypesCondition.Stunned))
			value -= 2;
		
		if (hasDexterity)
		{
			if (!character.hasCondition(GameTypesCondition.Blinded) &&
				!character.hasCondition(GameTypesCondition.Immobilized) &&
				!character.hasCondition(GameTypesCondition.Helpless) &&
				!character.hasCondition(GameTypesCondition.Stunned))
			{
				value += attribute.getAbilityModifier();
				value = checkConditionDefenseAlternateModifiers(value);
			}
		}
		
		GameResourceAttributeSkill skillTumble = character.getSkill(GameResourceAttributeSkill.SkillTypes.Tumble);
		double tumbleRanks = (skillTumble != null ? skillTumble.getRank() : 0);
		if (character.getCombatState() == GameResourceCharacter.CombatStates.FightingDefensively)
		{
			if (tumbleRanks >= 15)
				value += 4;
			else if ( tumbleRanks >= 5 )
				value += 3;
			else
				value += 2;
		}
		else if (character.getCombatState() == GameResourceCharacter.CombatStates.FightingDefensively)
		{
			if (tumbleRanks >= 15)
				value += 8;
			else if ( tumbleRanks >= 5 )
				value += 6;
			else
				value += 4;
		}
		
		return value;
	}
	
	public int checkConditionSkill(GameResourceAttributeSkill attribute, int value)
	{
		if (attribute.getCharacter().hasCondition(GameTypesCondition.Blinded) &&
			(attribute.getAbility().isTypeStrength() ||
			 attribute.getAbility().isTypeDexterity() ||
			 attribute.isTypeSearch()))
			value -= 4;
		
		if (attribute.getCharacter().hasCondition(GameTypesCondition.Frightened) ||
			attribute.getCharacter().hasCondition(GameTypesCondition.Panicked) ||
			attribute.getCharacter().hasCondition(GameTypesCondition.Shaken)||
			attribute.getCharacter().hasCondition(GameTypesCondition.Sickened))
			value -= 2;
		
		return value;
	}
	
	public GameResource getResource(int id)
	{
		return _resources.get(id);
	}
	
	public GameResource getResource(String name)
	{
		for (GameResource resource : _resources)
		{
			if (resource.getName().equalsIgnoreCase(name))
				return resource;
		}
		
		return null;
	}
	
	public boolean getSettingsDamageHealingSpecial()
	{
		return false;
	}
	
	public int getSettingsDamageType()
	{
		return GameTypesSetting.DamageType.HitPoints;
	}
	
	public boolean getSettingsDamageTypeHitPoints()
	{
		return (getSettingsDamageType() == GameTypesSetting.DamageType.HitPoints);
	}
	
	public boolean getSettingsDamageTypeVitalityPoints()
	{
		return (getSettingsDamageType() == GameTypesSetting.DamageType.VitalityPoints);
	}
	
	public int getSettingsDefenseType()
	{
		return GameTypesSetting.DefenseType.ArmorAsAC;
	}
	
	public boolean getSettingsDefenseTypeArmorAsAC()
	{
		return (getSettingsDefenseType() == GameTypesSetting.DefenseType.ArmorAsAC);
	}
	
	public boolean getSettingsDefenseTypeArmorAsDR()
	{
		return (getSettingsDefenseType() == GameTypesSetting.DefenseType.ArmorAsDR);
	}
	
	public int getSizeBonus(String value)
	{
		if (value == "C")
			return -8;
		if (value == "G")
			return -4;
		if (value == "H")
			return -2;
		if (value == "L")
			return -1;
		if (value == "S")
			return 1;
		if (value == "T")
			return 2;
		if (value == "D")
			return 4;
		if (value == "F")
			return 8;
		
		return 0;
	}
	
	public String getType()
	{
		return _type;
	}
	
	public int getTypeAbilityId(String abbr)
	{ 
		if ("STR".equalsIgnoreCase(abbr))
			return GameAbilities.Strength;
		if ("DEX".equalsIgnoreCase(abbr))
			return GameAbilities.Dexterity;
		if ("CON".equalsIgnoreCase(abbr))
			return GameAbilities.Constitution;
		if ("INT".equalsIgnoreCase(abbr))
			return GameAbilities.Intelligence;
		if ("WIS".equalsIgnoreCase(abbr))
			return GameAbilities.Wisdom;
		if ("CHA".equalsIgnoreCase(abbr))
			return GameAbilities.Charisma;
		
		return -1;
	}
	
	public int getTypeAbilityTitle(int id)
	{
		switch (id)
		{
			case GameAbilities.Strength:
				return R.string.title_game_type_ability_0;
			case GameAbilities.Dexterity:
				return R.string.title_game_type_ability_1;
			case GameAbilities.Constitution:
				return R.string.title_game_type_ability_2;
			case GameAbilities.Intelligence:
				return R.string.title_game_type_ability_3;
			case GameAbilities.Wisdom:
				return R.string.title_game_type_ability_4;
			case GameAbilities.Charisma:
				return R.string.title_game_type_ability_5;
		}
		
		return -1;
	}
	
	public String getTypeConditionImportance(int type)
	{
		switch (type)
		{
			case GameTypesCondition.Alive:
				return "Green";
			case GameTypesCondition.Dead:
				return "DarkRed";
			case GameTypesCondition.Disabled:
				return "Red";
			case GameTypesCondition.Dying:
				return "Red";
			case GameTypesCondition.Stable:
				return "Purple";
			case GameTypesCondition.Staggered:
				return "Orange";
			case GameTypesCondition.Unconscious:
				return "Orange";
			case GameTypesCondition.Stunned:
				return "Yellow";
			case GameTypesCondition.Bloodied:
				return "Red";
		}
		
		return "";
	}
	
	public int getTypeConditionOrder(int type)
	{
		switch (type)
		{
			case GameTypesCondition.Alive:
				return 1001;
			case GameTypesCondition.Dead:
				return 1002;
			case GameTypesCondition.Disabled:
				return 1003;
			case GameTypesCondition.Dying:
				return 1004;
			case GameTypesCondition.Stable:
				return 1005;
			case GameTypesCondition.Staggered:
				return 1006;
			case GameTypesCondition.Unconscious:
				return 1007;
			case GameTypesCondition.Stunned:
				return 1008;
			case GameTypesCondition.Bloodied:
				return 1009;
		}
		
		return 99999999;
	}
	
	public int getTypeConditionTitle(int type)
	{
		switch (type)
		{
			case GameTypesCondition.Alive:
				return R.string.title_game_type_condition_alive;
			case GameTypesCondition.Blinded:
				return R.string.title_game_type_condition_blinded;
			case GameTypesCondition.Bloodied:
				return R.string.title_game_type_condition_bloodied;
			case GameTypesCondition.Cowering:
				return R.string.title_game_type_condition_cowering;
			case GameTypesCondition.Dazed:
				return R.string.title_game_type_condition_dazed;
			case GameTypesCondition.Dazzeled:
				return R.string.title_game_type_condition_dazzeled;
			case GameTypesCondition.Dead:
				return R.string.title_game_type_condition_dead;
			case GameTypesCondition.Deafened:
				return R.string.title_game_type_condition_deafened;
			case GameTypesCondition.Diseased:
				return R.string.title_game_type_condition_diseased;
			case GameTypesCondition.Disabled:
				return R.string.title_game_type_condition_disabled;
			case GameTypesCondition.Dying:
				return R.string.title_game_type_condition_dying;
			case GameTypesCondition.Entangled:
				return R.string.title_game_type_condition_entangled;
			case GameTypesCondition.Exhausted:
				return R.string.title_game_type_condition_exhausted;
			case GameTypesCondition.Fascinated:
				return R.string.title_game_type_condition_fascinated;
			case GameTypesCondition.Fatigued:
				return R.string.title_game_type_condition_fatigued;
			case GameTypesCondition.Frightened:
				return R.string.title_game_type_condition_frightened;
			case GameTypesCondition.KnockedDown:
				return R.string.title_game_type_condition_knockeddown;
			case GameTypesCondition.Nauseated:
				return R.string.title_game_type_condition_nauseated;
			case GameTypesCondition.Panicked:
				return R.string.title_game_type_condition_panicked;
			case GameTypesCondition.Paralyzed:
				return R.string.title_game_type_condition_paralyzed;
			case GameTypesCondition.Poisoned:
				return R.string.title_game_type_condition_poisoned;
			case GameTypesCondition.Petrified:
				return R.string.title_game_type_condition_petrified;
			case GameTypesCondition.Shaken:
				return R.string.title_game_type_condition_shaken;
			case GameTypesCondition.Sickened:
				return R.string.title_game_type_condition_sickened;
			case GameTypesCondition.Stable:
				return R.string.title_game_type_condition_stable;
			case GameTypesCondition.Staggered:
				return R.string.title_game_type_condition_staggered;
			case GameTypesCondition.Stunned:
				return R.string.title_game_type_condition_stunned;
			case GameTypesCondition.Unconscious:
				return R.string.title_game_type_condition_unconscious;
		}
		
		return R.string.blank;
	}
	
	public ArrayList<GameResourceCondition> getTypeConditions()
	{
		ArrayList<Integer> types = getTypeConditionTypes();
		
		ArrayList<GameResourceCondition> list = new ArrayList<GameResourceCondition>();
		for (Integer type : types)
			list.add(new GameResourceCondition(type.intValue()));
		
		return list;
	}
	
	public ArrayList<Integer> getTypeConditionTypes()
	{
		ArrayList<Integer> list = new ArrayList<Integer>();
		
		list.add(GameTypesCondition.Alive);
		list.add(GameTypesCondition.Blinded);
		list.add(GameTypesCondition.Bloodied);
		list.add(GameTypesCondition.Cowering);
		list.add(GameTypesCondition.Dazed);
		list.add(GameTypesCondition.Dazzeled);
		list.add(GameTypesCondition.Dead);
		list.add(GameTypesCondition.Deafened);
		list.add(GameTypesCondition.Diseased);
		list.add(GameTypesCondition.Disabled);
		list.add(GameTypesCondition.Dying);
		list.add(GameTypesCondition.Entangled);
		list.add(GameTypesCondition.Exhausted);
		list.add(GameTypesCondition.Fascinated);
		list.add(GameTypesCondition.Fatigued);
		list.add(GameTypesCondition.Frightened);
		list.add(GameTypesCondition.KnockedDown);
		list.add(GameTypesCondition.Nauseated);
		list.add(GameTypesCondition.Panicked);
		list.add(GameTypesCondition.Paralyzed);
		list.add(GameTypesCondition.Poisoned);
		list.add(GameTypesCondition.Petrified);
		list.add(GameTypesCondition.Shaken);
		list.add(GameTypesCondition.Sickened);
		list.add(GameTypesCondition.Stable);
		list.add(GameTypesCondition.Staggered);
		list.add(GameTypesCondition.Stunned);
		list.add(GameTypesCondition.TemporaryDamaged);
		list.add(GameTypesCondition.TemporaryHealed);
		list.add(GameTypesCondition.Unconscious);
		
		return list;
	}
	
	public int getTypeInitiativeTitle()
	{
		return R.string.title_game_type_initiative;
	}
	
	public int getTypeDamagePrimaryTitle()
	{
		if (getSettingsDamageTypeVitalityPoints())
			return R.string.title_game_type_damage_vitality_points;
		
		return R.string.title_game_type_damage_hit_points;
	}
	
	public int getTypeHealingDamageSpecialTitle()
	{
		return R.string.blank;
	}
	
	public int getTypeDamagePrimaryTitleMax()
	{
		return R.string.title_game_type_damage_max_full;
	}
	
	public int getTypeDamagePrimaryTitleMin()
	{
		if (getSettingsDamageTypeVitalityPoints())
			return R.string.title_game_type_damage_min;
		
		return R.string.title_game_type_damage_min_death;
	}

	public int getTypeDamageSecondaryTitle()
	{
		if (getSettingsDamageTypeVitalityPoints())
			return R.string.title_game_type_damage_vitality_points_wound;
		
		return R.string.title_game_type_damage_hit_points_subdual;
	}

	public int getTypeDamageSecondaryTitleMax()
	{
		return R.string.title_game_type_damage_max_full;
	}

	public int getTypeDamageSecondaryTitleMin()
	{
		if (getSettingsDamageTypeVitalityPoints())
			return R.string.title_game_type_damage_min_death;
		
		return R.string.title_game_type_damage_min_unconscious;
	}
	
	public GameResourceList getResources()
	{
		return _resources;
	}
	
	public boolean hasResource(String name, ResourceTypes type)
	{
		for (GameResource resource : _resources)
		{
			if (resource.getName().equalsIgnoreCase(name) && (resource.getType() == type))
				return true;
		}
		
		return false;
	}
	
	public boolean isConditionClearable(int type)
	{
		switch (type)
		{
			case GameTypesCondition.Blinded:
			case GameTypesCondition.Cowering:
			case GameTypesCondition.Dazed:
			case GameTypesCondition.Deafened:
			case GameTypesCondition.Diseased:
			case GameTypesCondition.Entangled:
			case GameTypesCondition.Exhausted:
			case GameTypesCondition.Fatigued:
			case GameTypesCondition.Frightened:
			case GameTypesCondition.Immobilized:
			case GameTypesCondition.Helpless:
			case GameTypesCondition.Nauseated:
			case GameTypesCondition.Panicked:
			case GameTypesCondition.Paralyzed:
			case GameTypesCondition.Poisoned:
			case GameTypesCondition.Shaken:
			case GameTypesCondition.Sickened:
			case GameTypesCondition.Stunned:
				return true;
		}
		
		return false;
	}
	
	public boolean isConditionSettable(int type)
	{
		switch (type)
		{
			case GameTypesCondition.Blinded:
			case GameTypesCondition.Cowering:
			case GameTypesCondition.Dazed:
			case GameTypesCondition.Deafened:
			case GameTypesCondition.Diseased:
			case GameTypesCondition.Entangled:
			case GameTypesCondition.Exhausted:
			case GameTypesCondition.Fatigued:
			case GameTypesCondition.Frightened:
			case GameTypesCondition.Immobilized:
			case GameTypesCondition.Helpless:
			case GameTypesCondition.Nauseated:
			case GameTypesCondition.Panicked:
			case GameTypesCondition.Paralyzed:
			case GameTypesCondition.Poisoned:
			case GameTypesCondition.Shaken:
			case GameTypesCondition.Sickened:
			case GameTypesCondition.Stunned:
				return true;
		}
		
		return false;
	}
	
	public boolean isConditionVisible(int type)
	{
		switch (type)
		{
			case GameTypesCondition.Blinded:
			case GameTypesCondition.Bloodied:
			case GameTypesCondition.Cowering:
			case GameTypesCondition.Dazed:
			case GameTypesCondition.Dead:
			case GameTypesCondition.Deafened:
			case GameTypesCondition.Diseased:
			case GameTypesCondition.Disabled:
			case GameTypesCondition.Dying:
			case GameTypesCondition.Entangled:
			case GameTypesCondition.Exhausted:
			case GameTypesCondition.Fatigued:
			case GameTypesCondition.Frightened:
			case GameTypesCondition.KnockedDown:
			case GameTypesCondition.Nauseated:
			case GameTypesCondition.Panicked:
			case GameTypesCondition.Paralyzed:
			case GameTypesCondition.Poisoned:
			case GameTypesCondition.Shaken:
			case GameTypesCondition.Sickened:
			case GameTypesCondition.Stable:
			case GameTypesCondition.Staggered:
			case GameTypesCondition.Stunned:
			case GameTypesCondition.Unconscious:
				return true;
		}
		
		return false;
	}
	
	public void load(java.io.InputStream stream) 
		throws Throwable
	{
		load(stream, false);
	}
	
	public void load(java.io.InputStream stream, boolean folderInfoOnly) 
		throws Throwable
	{
		Validate.notNull(stream);
		
		try
    	{
			JSONObject data = JSONUtils.fromStream(stream);
    		
			setName(getJSONString(data, "name"));
			setType(getJSONString(data, "type"));

			if (!folderInfoOnly)
			{
				// see the initLoadItem, it determines if a different class
				// other than GameResource needs to be created.
				loadArray(_resources, data, "resources", GameResource.class);
				for (GameResource resource : _resources)
					resource.init(this);
			
				loadEx();
			}
		}
    	catch (Throwable tr)
    	{
    		Logger.e(TAG, "load", tr);
    		throw tr;
    	}
	}

	public JSONObject load(JSONObject object) 
		throws JSONException 
	{
		throw new UnsupportedOperationException(); 
	}
	
	public void removeResource(GameResource resource)
	{
		_resources.remove(resource);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	protected <E extends IResource> E initLoadItem(JSONObject itemJSON, Class<E> classType)
		throws Throwable
	{
		E item = super.initLoadItem(itemJSON, classType);
		
		GameResource resource = (GameResource)item;
		if (resource.getType() == GameResource.ResourceTypes.Character)
			item = (E)super.initLoadItem(itemJSON, GameResourceCharacter.class);
		
		return item;
	}
	
	public void save(java.io.OutputStream stream) 
		throws Throwable
	{
		try
    	{
			if (stream == null)
				return;
			
			JSONObject data = new JSONObject();
			data.put("name", _name);
			data.put("type", _type);
			
			saveArray(_resources, data, "resources");
			
			JSONUtils.toStream(data, stream);
		}
    	catch (Throwable tr)
    	{
    		Logger.e(TAG, "save", tr);
    		throw tr;
    	}
	}

	public JSONObject save() 
		throws JSONException 
	{
		throw new UnsupportedOperationException(); 
	}
	
	public void setName(String name)
	{
		_name = name;
	}
	
	public static final Parcelable.Creator<GameFolder> CREATOR = new Parcelable.Creator<GameFolder>() 
	{
		public GameFolder createFromParcel(Parcel in) 
		{
		    return new GameFolder(in);
		}
		
		public GameFolder[] newArray(int size) 
		{
		    return new GameFolder[size];
		}
	};
	
	protected int checkConditionDefenseAlternateModifiers(int value)
	{
		return value;
	}
	
	protected boolean isType(String type)
	{
		if (StringUtils.isEmpty(type))
			return false;

		return getType().equalsIgnoreCase(type);
	}
	
	protected void loadEx()
	{
		
	}
	
	protected void loadParcel()
	{
		
	}

	@Override
	protected void readFromParcelTransform(Parcel in) 
		throws Throwable
	{
		try
		{
			setName(in.readString());
			setType(in.readString());
			
			loadParcel();
			
			_resources = (GameResourceList)in.readParcelable(GameResourceList.class.getClassLoader());
			for (GameResource resource : _resources)
				resource.init(this);
		}
    	catch (Throwable tr)
    	{
    		Logger.e(TAG, "readFromParcelTransform", tr);
    		throw tr;
    	}
	}
	
	protected void saveEx()
	{
		
	}
	
	protected void saveParcel()
	{
		
	}

	@Override
	protected void writeToParcelTransform(Parcel dest, int flags)
		throws Throwable
	{
		try
		{
			dest.writeString(_name);
			dest.writeString(_type);
			
			saveParcel();
			
			dest.writeParcelable(_resources, flags);	
		}
    	catch (Throwable tr)
    	{
    		Logger.e(TAG, "writeToParcelTransform", tr);
    		throw tr;
    	}
	}
	
	private void setType(String type)
	{
		_type = type;
	}
	
	private String _name;
	private GameResourceList _resources = new GameResourceList();
	private String _type;

	private static final String TAG = GameFolder.class.getSimpleName();
}
